/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.impl;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.compiler.impl.JdkCompiler;
import cn.easyplatform.lang.Dumps;
import cn.easyplatform.lang.Lang;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.*;
import cn.easyplatform.messages.response.*;
import cn.easyplatform.messages.vos.*;
import cn.easyplatform.messages.vos.datalist.ListBatchVo;
import cn.easyplatform.messages.vos.executor.EndVo;
import cn.easyplatform.messages.vos.executor.ErrorVo;
import cn.easyplatform.spi.service.ComponentService;
import cn.easyplatform.spi.service.TaskService;
import cn.easyplatform.type.Constants;
import cn.easyplatform.type.FieldVo;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.web.contexts.Contexts;
import cn.easyplatform.web.dialog.ActionEvent;
import cn.easyplatform.web.dialog.MessageBox;
import cn.easyplatform.web.dialog.ProgressDialog;
import cn.easyplatform.web.ext.*;
import cn.easyplatform.web.layout.IMainTaskBuilder;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.BackendException;
import cn.easyplatform.web.task.MainTaskSupport;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.PageHandler;
import cn.easyplatform.web.task.api.DefaultPageContext;
import cn.easyplatform.web.task.event.AccValueChangeEventListener;
import cn.easyplatform.web.task.event.EventEntry;
import cn.easyplatform.web.task.event.EventListenerHandler;
import cn.easyplatform.web.task.event.FieldEntry;
import cn.easyplatform.web.task.support.ManagedComponent;
import cn.easyplatform.web.task.zkex.ComponentBuilderFactory;
import cn.easyplatform.web.task.zkex.ReportSupport;
import cn.easyplatform.web.task.zkex.list.OperableListSupport;
import cn.easyplatform.web.utils.PageUtils;
import cn.easyplatform.web.utils.TaskInfo;
import cn.easyplatform.web.utils.WebUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.zk.ui.*;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Combobox;
import org.zkoss.zul.Idspace;
import org.zkoss.zul.impl.FormatInputElement;
import org.zkoss.zul.impl.InputElement;
import org.zkoss.zul.impl.XulElement;

import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
abstract class AbstractMainTaskBuilder<T extends HtmlBasedComponent> implements IMainTaskBuilder, MainTaskSupport, PageHandler, EventListener<Event> {

    protected final static Logger log = LoggerFactory.getLogger(AbstractMainTaskBuilder.class);

    private OperableHandler host;

    protected Map<String, Component> managedComponents;

    private List<ManagedComponent> managedEntityComponents;

    private String taskId;

    protected List<Component> managedInputComponents;

    protected AbstractPageVo apv;

    protected T idSpace;

    protected Component container;

    private ProgressDialog mc;

    private String processCode = "";

    private Map<String, MainTaskSupport> children;

    private EventListener<Event> destinationListener;

    private List<Destroyable> destroyables;

    private String[] access;

    /**
     * @param container
     * @param taskId    ：参数TaskBean的ID
     * @param apv
     */
    AbstractMainTaskBuilder(Component container, String taskId,
                            AbstractPageVo apv) {
        this.container = container;
        this.taskId = taskId;
        this.apv = apv;
        this.access = apv instanceof PageVo ? ((PageVo) apv).getAccess() : null;
        managedComponents = new HashMap<String, Component>();
        managedInputComponents = new ArrayList<Component>();
        managedEntityComponents = new ArrayList<ManagedComponent>();
        children = new HashMap<String, MainTaskSupport>();
    }

    @Override
    public void build() {
        idSpace = create();
        idSpace.setPage(container.getPage());
        if (apv instanceof PageVo)
            doPage();
        else if (apv instanceof BatchVo)
            doBatch();
    }

    protected T create() {
        Idspace ids = new Idspace();
        //ids.setVflex("1");
        //ids.setHflex("1");
        ids.setZclass("w-100 h-100");
        return (T) ids;
    }

    protected void doBatch() {
        mc = new ProgressDialog(container.getPage());
        Contexts.subscribe(getId(), this);
    }

    protected void doPage() {
        PageVo pv = (PageVo) apv;
        processCode = pv.getProcessCode();
        Component root = null;
        try {
            //设置$easyplatform系统变量
            Object platform = container.getPage().getXelVariable("$easyplatform");
            if (platform == null)
                platform = container.getDesktop().getFirstPage().getXelVariable("$easyplatform");
            if (platform != null)
                idSpace.setAttribute("$easyplatform", platform);
            //准备绑定变量
            if (pv.getBindVariables() != null) {
                for (Map.Entry<String, Object> entry : pv.getBindVariables().entrySet())
                    PageUtils.bindVariable(entry.getKey(), entry.getValue(), idSpace);
            }
            //初始事件
            if (!Strings.isBlank(pv.getOnShow())) {
                new EventListenerHandler(Events.ON_USER,
                        this, pv.getOnShow()).onEvent(new Event(Events.ON_USER, idSpace));
            }
            //创建页面
            root = Executions.getCurrent().createComponentsDirectly(
                    pv.getPage(), "zul", idSpace, null);

            if (Strings.isBlank(pv.getApply())) {
                doPage(root);
                if (pv.isVisible()) {
                    if (processCode.equals("D") || processCode.equals("R"))
                        PageUtils.disabled(managedInputComponents);
                    if (!Strings.isBlank(pv.getStyle())) {
                        HtmlNativeComponent style = new HtmlNativeComponent("style", pv.getStyle(), "");
                        style.setStubonly(true);
                        idSpace.appendChild(style);
                    }
                    if (!Strings.isBlank(pv.getJavascript())) {
                        HtmlNativeComponent script = new HtmlNativeComponent("script", pv.getJavascript(), "");
                        script.setStubonly(true);
                        idSpace.appendChild(script);
                    }
                    // 执行后置事件
                    if (!Strings.isBlank(pv.getOnVisible())) {
                        String event = root.getEvent();
                        ((AbstractComponent) root).setEvent(pv.getOnVisible());
                        PageUtils.addEventListener(this, Events.ON_USER, root);
                        Events.sendEvent(new Event(Events.ON_USER, root));
                        ((AbstractComponent) root).setEvent(event);
                    }
                    //订阅消息
                    if (!Strings.isBlank(pv.getOnMessage()) && !Strings.isBlank(pv.getDestination()))
                        subscribe(pv.getDestination());
                } else {
                    // 模拟发动一个确认的动作
                    ((AbstractComponent) root).setEvent("next()");
                    PageUtils.addEventListener(this, Events.ON_USER, root);
                    Events.postEvent(new Event(Events.ON_USER, root));
                }
            } else {//自定义实现类，一般处理复杂的页面
                JdkCompiler compiler = new JdkCompiler();
                Class<?> clazz = compiler.compile(pv.getApply(), this.getClass().getClassLoader());
                if (ApplyPage.class.isAssignableFrom(clazz)) {
                    ApplyPage apply = (ApplyPage) clazz.newInstance();
                    apply.init(new DefaultPageContext(this), root);
                }
            }
        } catch (Exception e) {
            if (log.isErrorEnabled())
                log.error("doPage", e);
            if (idSpace.getFirstChild() != null)
                idSpace.getFirstChild().detach();
            //idspace.setPage(null);
            if (e instanceof RuntimeException)
                throw (RuntimeException) e;
            throw new RuntimeException(e);
        }
    }

    /**
     * 动态解析
     *
     * @param root
     */
    public void doPage(Component root) {
        PageVo pv = (PageVo) apv;
        Iterator<Component> itr = root.queryAll("*").iterator();
        List<Component> extList = new ArrayList<Component>();
        while (itr.hasNext()) {
            Component comp = itr.next();
            if (pv.isVisible() && access != null && !Strings.isBlank(comp.getAccess())) {
                if (!ArrayUtils.contains(access, comp.getAccess())) {
                    if (comp.getAccess().endsWith("_V")) {
                        comp.detach();
                        continue;
                    } else if (!(comp instanceof EntityExt) && !(comp instanceof ZkExt)) {
                        PageUtils.disabled(comp);
                    }
                }
            }
            if (comp instanceof EntityExt || comp instanceof ZkExt)
                extList.add(comp);
            else if (pv.isVisible()) {
                if (comp.getId().startsWith("#")) // 绑定的变量或栏位
                    setComponentValue(comp, pv.getFields());
                else if (!Strings.isBlank(comp.getId()))// 所有id不为空的都是受管组件
                    managedComponents.put(comp.getId(), comp);
                PageUtils.processEventHandler(this, comp, null);
            }// else if
        }// while
        //
        for (Component comp : extList) {
            if (comp instanceof ManagedExt) {
                ComponentBuilder builder;
                if (comp instanceof EntityExt)
                    builder = ComponentBuilderFactory.getEntityBuilder(
                            this, (EntityExt) comp);
                else
                    builder = ComponentBuilderFactory.getZkBuilder(this,
                            (ZkExt) comp);
                Component c = builder.build();
                if (c != null) {
                    if (builder instanceof Destroyable) {
                        if (destroyables == null)
                            destroyables = new ArrayList<>();
                        destroyables.add((Destroyable) builder);
                    }
                    if (builder instanceof ManagedComponent) {
                        managedEntityComponents.add((ManagedComponent) builder);
                        if (root == comp)
                            root = ((ManagedComponent) builder).getComponent();
                    } else {
                        managedComponents.put(comp.getId(), comp);
                        if (root == comp)
                            root = c;
                    }
                }
            } else if (comp instanceof ZkExt) {
                ComponentBuilder builder = ComponentBuilderFactory.getZkBuilder(this,
                        (ZkExt) comp);
                //设置字典
                if (comp instanceof Queryable && comp.getId().startsWith("#")) {
                    for (FieldVo fv : pv.getFields()) {
                        if (fv.getName().equals(comp.getId().substring(1))) {
                            ((Queryable) comp).setOptionQuery(fv);
                            break;
                        }
                    }
                }
                builder.build();
                if (builder instanceof Destroyable) {
                    if (destroyables == null)
                        destroyables = new ArrayList<>();
                    destroyables.add((Destroyable) builder);
                }
            }
            if (comp.getId().startsWith("#") && pv.isVisible())
                setComponentValue(comp, pv.getFields());
            else if (!Strings.isBlank(comp.getId())
                    && !(comp instanceof ManagedExt))
                managedComponents.put(comp.getId(), comp);
        }
        PageUtils.save(managedInputComponents);
        extList = null;
    }

    private void setComponentValue(Component comp, List<FieldVo> tmpList) {
        String id = comp.getId().substring(1);
        Iterator<FieldVo> tmpItr = tmpList.iterator();
        while (tmpItr.hasNext()) {
            FieldVo fv = tmpItr.next();
            if (fv.getName().equals(id)) {
                if (!Strings.isBlank(fv.getAcc())) {//有关联acc栏位
                    Component target = comp.getFellowIfAny('#' + fv.getAcc());
                    if (target != null && target instanceof InputElement)
                        target.addEventListener(Events.ON_CHANGE, new AccValueChangeEventListener(apv.getId(), comp));
                }
                if (comp instanceof InputElement) {
                    InputElement input = (InputElement) comp;
                    if (input.getMaxlength() == 0
                            && !(input instanceof Combobox))
                        input.setMaxlength(fv.getLength());
                    if (input instanceof FormatInputElement) {
                        FormatInputElement format = (FormatInputElement) input;
                        if (Strings.isBlank(format.getFormat()))
                            format.setFormat(WebUtils.getFormat(fv));
                    }
                    if (input.getConstraint() != null) {
                        try {
                            PageUtils.setValue(comp, fv.getValue());
                        } catch (WrongValueException ex) {
                            // ignore here
                        }
                    } else
                        PageUtils.setValue(comp, fv.getValue());
                } else
                    PageUtils.setValue(comp, fv.getValue());
                if ((comp instanceof XulElement)
                        && Strings
                        .isBlank(((XulElement) comp).getTooltiptext()))
                    ((XulElement) comp).setTooltiptext(id);
                //tmpItr.remove();
                managedInputComponents.add(comp);
                break;
            }
        }
        comp.setId(id);
        managedComponents.put(id, comp);
    }

    @Override
    public void subscribe(String topics) {
        IResponseMessage<?> resp = ServiceLocator.lookup(TaskService.class).subscribe(new SimpleTextRequestMessage(topics));
        if (resp.isSuccess()) {
            PageVo pv = (PageVo) this.apv;
            final EventListenerHandler elh = new EventListenerHandler("onTopic", this, pv.getOnMessage());
            destinationListener = new EventListener() {
                @Override
                public void onEvent(Event event) throws Exception {
                    elh.onEvent(new Event(event.getName(), idSpace, event.getData()));
                }
            };
            String[] array = topics.split(",");
            for (String topic : array)
                Contexts.subscribe(topic, destinationListener);
            pv.setDestination(topics);
        }
    }

    @Override
    public void unsubscribe() {
        String destination = ((PageVo) apv).getDestination();
        if (Strings.isBlank(destination))
            ServiceLocator.lookup(TaskService.class).unsubscribe(new SimpleTextRequestMessage(destination));
        String[] array = destination.split(",");
        for (String topic : array)
            Contexts.unsubscribe(topic, destinationListener);
        destinationListener = null;
    }

    @Override
    public void refresh(EventEntry<FieldEntry> entry) {
        String[] fields = entry.getEntry().getFields();
        int actionFlag = entry.getActionFlag();
        if (log.isDebugEnabled())
            log.debug("refresh->{}", Dumps.obj(fields));
        if (fields.length == 0) {// 刷新全部
            if (actionFlag == 0) {
                for (MainTaskSupport ts : children.values())
                    ((OperableHandler) ts).refresh(entry);
            }
            Map<String, Object> data = new HashMap<String, Object>();
            if (actionFlag >= 0) {
                for (Component comp : managedInputComponents) {
                    data.put(comp.getId(),
                            actionFlag == 0 ? PageUtils.getValue(comp, true)
                                    : null);
                }
            }
            RefreshRequestMessage req = new RefreshRequestMessage(getId(), data);
            req.setActionFlag(actionFlag);
            IResponseMessage<?> resp = ServiceLocator.lookup(
                    TaskService.class).refresh(req);
            if (actionFlag == -1)
                return;
            if (resp.isSuccess()) {
                if (resp instanceof FieldsUpdateResponseMessage) {
                    Map<String, Object> map = ((FieldsUpdateResponseMessage) resp)
                            .getBody();
                    for (Component comp : managedInputComponents)
                        PageUtils.setValue(comp, map.get(comp.getId()));
                }
            } else
                throw new BackendException(resp);
        } else {
            IResponseMessage<?> resp = ServiceLocator.lookup(
                    TaskService.class).getFields(
                    new GetFieldsRequestMessage(getId(), fields));
            if (resp.isSuccess()) {
                if (resp instanceof FieldsUpdateResponseMessage) {
                    Map<String, Object> map = ((FieldsUpdateResponseMessage) resp)
                            .getBody();
                    for (Component comp : managedInputComponents) {
                        for (Iterator<Map.Entry<String, Object>> itr = map.entrySet().iterator(); itr.hasNext(); ) {
                            Map.Entry<String, Object> e = itr.next();
                            if (comp.getId().equals(e.getKey())) {
                                Object value = PageUtils.getValue(comp, false);
                                if (!Lang.equals(value, e.getValue())) {
                                    PageUtils.setValue(comp, e.getValue());
                                    if (entry.getEntry().isSendEvent()
                                            && comp.getEventListeners(Events.ON_CHANGE).iterator().hasNext()) {
                                        Event evt = new Event(Events.ON_CHANGE,
                                                comp);
                                        Events.sendEvent(comp, evt);
                                    }
                                }
                                itr.remove();
                                break;
                            }
                        }
                        if (map.isEmpty())
                            break;
                    }
                }
            } else
                throw new BackendException(resp);
        }
    }

    @Override
    public void reload(String... comps) {
        if (log.isDebugEnabled())
            log.debug("reload->{}", Dumps.obj(comps));
        if (comps.length == 0) {// 由清单触发
            List<String> fields = new ArrayList<String>();
            for (Component comp : managedInputComponents)
                fields.add(comp.getId());
            ReloadRequestMessage req = new ReloadRequestMessage(getId(),
                    fields.toArray(new String[fields.size()]));
            IResponseMessage<?> resp = ServiceLocator.lookup(
                    TaskService.class).reload(req);
            if (!resp.isSuccess())
                throw new BackendException(resp);
            for (Component comp : managedComponents.values()) {
                if (comp instanceof Reloadable) {
                    Reloadable widget = (Reloadable) comp;
                    if (widget.isForce())
                        widget.reload();
                }
            }
            Map<String, Object> map = ((FieldsUpdateResponseMessage) resp)
                    .getBody();
            for (Component comp : managedInputComponents)
                PageUtils.setValue(comp, map.get(comp.getId()));
            if (processCode.equals("R"))
                PageUtils.disabled(managedInputComponents);
            else
                PageUtils.reset(managedInputComponents);
            for (ManagedComponent es : managedEntityComponents) {
                if (es instanceof OperableListSupport) {
                    OperableListSupport os = (OperableListSupport) es;
                    if ((os.getEntity().getType().equals(Constants.CATALOG) && !os
                            .getEntity().isDurable())
                            || os.getEntity().getType()
                            .equals(Constants.DETAIL)) {
                        os.reset(true);
                        if (Strings.isBlank(os.getEntity().getHost()))
                            os.reload(new String[0]);
                    }
                } else if (es instanceof ReportSupport)
                    ((ReportSupport) es).reload();
            }
            // 执行后置事件
            if (!Strings.isBlank(((PageVo) apv).getOnVisible()))
                Events.postEvent(new Event(Events.ON_USER, idSpace.getFirstChild()));
        } else {
            if (comps[0].equals("*")) {// 刷新主页面栏位
                for (Component comp : managedComponents.values()) {
                    if (comp instanceof Reloadable) {
                        Reloadable widget = (Reloadable) comp;
                        if (widget.isForce())
                            widget.reload();
                    }
                }
                String[] fields = new String[managedInputComponents.size()];
                int index = 0;
                for (Component comp : managedInputComponents)
                    fields[index++] = comp.getId();
                TaskService tc = ServiceLocator
                        .lookup(TaskService.class);
                IResponseMessage<?> resp = tc
                        .getFields(new GetFieldsRequestMessage(getId(), fields));
                if (!resp.isSuccess())
                    throw new BackendException(resp);
                Map<String, Object> map = ((FieldsUpdateResponseMessage) resp)
                        .getBody();
                for (Component comp : managedInputComponents)
                    PageUtils.setValue(comp, map.get(comp.getId()));
            } else {
                for (String id : comps) {
                    boolean isFinded = false;
                    for (Component comp : managedInputComponents) {
                        if (comp.getId().equals(id)) {
                            if (comp instanceof Reloadable)
                                ((Reloadable) comp).reload();
                            isFinded = true;
                            break;
                        }
                    }
                    if (!isFinded) {
                        for (ManagedComponent es : managedEntityComponents) {
                            if (id.equals(es.getComponent().getId())) {
                                if (es instanceof OperableListSupport) {
                                    OperableListSupport os = (OperableListSupport) es;
                                    if ((os.getEntity().getType()
                                            .equals(Constants.CATALOG) && !os
                                            .getEntity().isDurable())
                                            || os.getEntity().getType()
                                            .equals(Constants.DETAIL)) {
                                        os.clear();
                                        if (Strings.isBlank(os.getEntity()
                                                .getHost()))
                                            os.reload(new String[0]);
                                    }
                                } else if (es instanceof ReportSupport)
                                    ((ReportSupport) es).reload();
                            }
                        }
                    }
                }// for
            }// if
        }// if
    }

    @Override
    public void update(String... comps) {
        if (log.isDebugEnabled())
            log.debug("update->{}", Dumps.obj(comps));
        if (comps.length == 0)
            return;
        List<FieldUpdateVo> vcs = new ArrayList<FieldUpdateVo>();
        for (Component comp : managedInputComponents) {
            if (comps[0].equals("*")) {
                vcs.add(new FieldUpdateVo(comp.getId(), PageUtils.getValue(
                        comp, false)));
            } else {
                for (int i = 0; i < comps.length; i++) {
                    if (comp.getId().equals(comps[i])) {
                        vcs.add(new FieldUpdateVo(comps[i], PageUtils.getValue(
                                comp, false)));
                        break;
                    }
                }
            }
        }
        SimpleRequestMessage req = new SimpleRequestMessage(getId(), vcs);
        IResponseMessage<?> resp = ServiceLocator.lookup(
                ComponentService.class).fieldUpdate(req);
        if (!resp.isSuccess())
            throw new BackendException(resp);
    }

    @Override
    public void save(EventEntry<Boolean> entry) {
        if (log.isDebugEnabled())
            log.debug("save->{}", entry.getEntry());
        int actionFlag = entry.getActionFlag();
        if (actionFlag == 0) {
            for (MainTaskSupport ts : children.values())
                ts.save(entry);
        }
        Map<String, Object> data = new HashMap<String, Object>();
        if (actionFlag == 0) {
            for (Component comp : managedInputComponents)
                data.put(comp.getId(), PageUtils.getValue(comp, true));
        }
        SaveRequestMessage req = new SaveRequestMessage(getId(), data,
                entry.getEntry());
        req.setActionFlag(actionFlag);
        req.setId(apv.getId());
        IResponseMessage<?> resp = ServiceLocator.lookup(
                TaskService.class).save(req);
        if (actionFlag == -1)
            return;
        if (resp.isSuccess()) {
            if (resp instanceof ListOpenResultResponseMessage) {
                ((OperableListSupport) getParent())
                        .update(((ListOpenResultResponseMessage) resp)
                                .getBody());
            } else if (resp instanceof FieldsUpdateResponseMessage) {
                Map<String, Object> map = ((FieldsUpdateResponseMessage) resp)
                        .getBody();
                for (Component comp : managedInputComponents)
                    PageUtils.setValue(comp, map.get(comp.getId()));
            } else if (resp instanceof SimpleResponseMessage)
                close(true);
        } else
            throw new BackendException(resp);
    }

    @Override
    public void prev(int actionFlag) {
        if (actionFlag == Integer.MAX_VALUE) {//来自移动端的返回事件
            PageVo pv = (PageVo) apv;
            List<String> fields = new ArrayList<>();
            for (FieldVo fv : pv.getFields())
                fields.add(fv.getName());
            SimpleRequestMessage req = new SimpleRequestMessage(getId(), fields.toArray(new String[fields.size()]));
            IResponseMessage<?> resp = ServiceLocator.lookup(
                    TaskService.class).prev(req);
            if (resp.isSuccess()) {
                Map<String, Object> data = (Map<String, Object>) resp.getBody();
                for (FieldVo fv : pv.getFields()) {
                    if (data.containsKey(fv.getName()))
                        fv.setValue(data.get(fv.getName()));
                }
                if (pv.getBindVariables() != null) {
                    for (Map.Entry entry : pv.getBindVariables().entrySet()) {
                        if (data.containsKey(entry.getKey()))
                            entry.setValue(data.get(entry.getKey()));
                    }
                }
                clear();
                idSpace.detach();
                idSpace = create();
                idSpace.setPage(container.getPage());
                doPage();
                container.appendChild(idSpace);
            } else
                throw new BackendException(resp);
        } else {
            SimpleRequestMessage req = new SimpleRequestMessage(getId(), null);
            req.setActionFlag(actionFlag);
            IResponseMessage<?> resp = ServiceLocator.lookup(
                    TaskService.class).prev(req);
            if (resp.isSuccess()) {
                if (resp instanceof SimpleResponseMessage) {
                    close(true);
                } else if (resp instanceof PageResponseMessage) {
                    clear();
                    apv = ((PageResponseMessage) resp).getBody();
                    doPage();
                }
            } else
                throw new BackendException(resp);
        }
    }

    @Override
    public void next(EventEntry<NextVo> entry) {
        int actionFlag = entry.getActionFlag();
        if (actionFlag == 0) {
            for (MainTaskSupport ts : children.values())
                ts.next(entry);
        }
        NextVo vo = entry.getEntry();
        // if (!(entry.getEntry() instanceof ListNextVo)) {
        Map<String, Object> data = new HashMap<String, Object>();
        if (actionFlag == 0 && isVisible()) {
            for (Component comp : managedInputComponents)
                data.put(comp.getId(), PageUtils.getValue(comp, true));
        }
        vo.setData(data);
        // }
        next(vo, actionFlag);
    }

    private void next(NextVo body, int actionFlag) {
        if (log.isDebugEnabled())
            log.debug("next->{}", body);
        NextRequestMessage req = new NextRequestMessage(getId(), body);
        req.setActionFlag(actionFlag);
        IResponseMessage<?> resp = ServiceLocator.lookup(
                TaskService.class).next(req);
        if (actionFlag == -1)
            return;
        doNext(resp);
    }

    private void doNext(IResponseMessage<?> resp) {
        if (resp.isSuccess()) {
            if (resp instanceof SimpleResponseMessage) {
                if (apv.getOpenModel() != Constants.OPEN_EMBBED)
                    close(true);
            } else {
                if (resp.isRedo()) {
                    // 重新执行功能
                    WebUtils.removeTask(getId());
                    apv.setId(((AbstractPageVo) resp.getBody()).getId());
                    TaskInfo taskInfo = new TaskInfo(taskId);
                    taskInfo.setId(apv.getId());
                    taskInfo.setTitle(apv.getTitile());
                    taskInfo.setImage(apv.getImage());
                    WebUtils.registryTask(taskInfo);
                }
                if (resp instanceof PageResponseMessage) {
                    clear();
                    apv = ((PageResponseMessage) resp).getBody();
                    doPage();
                } else if (resp instanceof BatchResponseMessage) {
                    apv = (BatchVo) resp.getBody();
                    doBatch();
                } else if (resp instanceof TaskNextErrorResponseMessage) {
                    TaskNextErrorResponseMessage tm = (TaskNextErrorResponseMessage) resp;
                    IResponseMessage<?> result = tm.getBody();
                    IResponseMessage<?> error = tm.getError();
                    if (result.isSuccess()) {
                        if (result instanceof PageResponseMessage) {
                            clear();
                            apv = ((PageResponseMessage) result)
                                    .getBody();
                            doPage();
                        } else {
                            close(true);
                        }
                    }
                    throw new BackendException(error);
                }
            }
        } else {
            if (resp.isRedo()) // 重新启动功能
                new RedoTaskListener(resp);
            else
                throw new BackendException(resp);
        }
    }

    @Override
    public void batch(ListBatchVo entry) {
        ListBatchRequestMessage req = new ListBatchRequestMessage(getId(),
                entry);
        IResponseMessage<?> resp = ServiceLocator.lookup(
                TaskService.class).batch(req);
        doNext(resp);
    }

    @Override
    public void onEvent(Event evt) {
        Object msg = evt.getData();
        mc.display(msg);
        if (msg instanceof EndVo || msg instanceof ErrorVo) {
            Contexts.unsubscribe(getId(), this);
            if (msg instanceof ErrorVo) {
                prev(0);
            } else {
                next(new NextVo(null), 0);
            }
            mc = null;
        }
    }

    @Override
    public void copy(EventEntry<String> entry) {
        // replica
        ActionVo av = new ActionVo("R");
        create(av);
    }

    @Override
    public void create(EventEntry<String> entry) {
        ActionVo av = new ActionVo("C");
        create(av);
    }

    private void create(ActionVo av) {
        List<String> inputs = new ArrayList<String>();
        for (Component comp : managedInputComponents)
            inputs.add(comp.getId());
        av.setFields(inputs.toArray(new String[inputs.size()]));
        IResponseMessage<?> resp = ServiceLocator.lookup(
                TaskService.class).action(
                new ActionRequestMessage(apv.getId(), av));
        if (!resp.isSuccess())
            throw new BackendException(resp);
        processCode = "C";
        Map<String, Object> fieldValues = ((FieldsUpdateResponseMessage) resp)
                .getBody();
        for (Component comp : managedInputComponents)
            PageUtils.setValue(comp, fieldValues.get(comp.getId()));
        PageUtils.reset(managedInputComponents);
        for (ManagedComponent es : managedEntityComponents) {
            if (es instanceof OperableListSupport) {
                OperableListSupport os = (OperableListSupport) es;
                if ((os.getEntity().getType().equals(Constants.CATALOG) && !os
                        .getEntity().isDurable())
                        || os.getEntity().getType().equals(Constants.DETAIL)) {
                    os.reset(true);
                    if (Strings.isBlank(os.getEntity().getHost()))
                        os.reload(new String[0]);
                }
            } else if (es instanceof ReportSupport)
                ((ReportSupport) es).reload();
        }
        for (Component comp : managedComponents.values()) {
            if (comp instanceof Reloadable) {
                Reloadable widget = (Reloadable) comp;
                if (widget.isForce())
                    widget.reload();
            }
        }
        // 执行后置事件
        if (!Strings.isBlank(((PageVo) apv).getOnVisible()))
            Events.postEvent(new Event(Events.ON_USER, idSpace.getFirstChild()));
    }

    @Override
    public void delete(EventEntry<String> entry) {
        IResponseMessage<?> resp = ServiceLocator.lookup(
                TaskService.class).action(
                new ActionRequestMessage(apv.getId(), new ActionVo("D")));
        if (!resp.isSuccess())
            throw new BackendException(resp);
        processCode = "D";
        PageUtils.disabled(managedInputComponents);
        for (ManagedComponent es : managedEntityComponents) {
            if (es instanceof OperableListSupport) {
                OperableListSupport os = (OperableListSupport) es;
                if ((os.getEntity().getType().equals(Constants.CATALOG) && !os
                        .getEntity().isDurable())
                        || os.getEntity().getType().equals(Constants.DETAIL))
                    os.reset(true);
            }
        }
        // 执行后置事件
        if (!Strings.isBlank(((PageVo) apv).getOnVisible()))
            Events.postEvent(new Event(Events.ON_USER, idSpace.getFirstChild()));
    }

    @Override
    public void edit(EventEntry<String> entry) {
        IResponseMessage<?> resp = ServiceLocator.lookup(
                TaskService.class).action(
                new ActionRequestMessage(apv.getId(), new ActionVo("U")));
        if (!resp.isSuccess())
            throw new BackendException(resp);
        processCode = "U";
        PageUtils.reset(managedInputComponents);
        for (ManagedComponent es : managedEntityComponents) {
            if (es instanceof OperableListSupport) {
                OperableListSupport os = (OperableListSupport) es;
                if ((os.getEntity().getType().equals(Constants.CATALOG) && !os
                        .getEntity().isDurable())
                        || os.getEntity().getType().equals(Constants.DETAIL)) {
                    os.reset(true);
                    if (Strings.isBlank(os.getEntity().getHost()))
                        os.reload(new String[0]);
                }
            } else if (es instanceof ReportSupport)
                ((ReportSupport) es).reload();
        }
        // 执行后置事件
        if (!Strings.isBlank(((PageVo) apv).getOnVisible()))
            Events.postEvent(new Event(Events.ON_USER, idSpace.getFirstChild()));
    }

    protected void clear() {
        managedComponents.clear();
        managedInputComponents.clear();
        managedEntityComponents.clear();
        if (destinationListener != null)
            unsubscribe();
        for (MainTaskSupport support : children.values())
            ((AbstractMainTaskBuilder) support).clear();
        children.clear();
        if (destroyables != null) {
            for (Destroyable es : destroyables)
                es.destory();
            destroyables.clear();
        }
        if (idSpace != null && !idSpace.isInvalidated())
            idSpace.getChildren().clear();
        WebUtils.removeChildren(getId());
    }

    @Override
    public OperableHandler getHost() {
        return host;
    }

    @Override
    public String getId() {
        return apv.getId();
    }

    @Override
    public String getTaskId() {
        return taskId;
    }

    @Override
    public String getName() {
        return apv.getTitile();
    }

    @Override
    public boolean isVisible() {
        return (apv instanceof PageVo) ? ((PageVo) apv).isVisible() : true;
    }

    @Override
    public void appendChild(String id, MainTaskSupport child) {
        children.put(id, child);
        ((IMainTaskBuilder) child).setHost(this);
    }

    @Override
    public void setHost(OperableHandler host) {
        this.host = host;
    }

    @Override
    public OperableHandler getParent() {
        return host;
    }

    @Override
    public Map<String, MainTaskSupport> getChildren() {
        return children;
    }

    @Override
    public Map<String, Component> getManagedComponents() {
        return managedComponents;
    }

    @Override
    public Collection<Component> getManagedInputComponents() {
        return managedInputComponents;
    }

    @Override
    public ManagedComponent getManagedEntityComponent(String id) {
        for (ManagedComponent c : managedEntityComponents) {
            if (c.getComponent().getId().equals(id))
                return c;
        }
        return null;
    }

    @Override
    public String getSourceCode() {
        if (apv instanceof PageVo)
            return ((PageVo) apv).getPage();
        return "";
    }

    @Override
    public void set(String name, Object value) {
        idSpace.setAttribute("#" + name, value);
    }

    @Override
    public Object get(String name) {
        return idSpace.getAttribute("#" + name);
    }

    @Override
    public Object remove(String name) {
        return idSpace.removeAttribute("#" + name);
    }

    @Override
    public Collection<ManagedComponent> getManagedEntityComponents() {
        return managedEntityComponents;
    }

    @Override
    public Component getComponent() {
        return idSpace;
    }

    @Override
    public String getProcessCode() {
        return processCode;
    }

    @Override
    public void setProcessCode(String code) {
        this.processCode = code;
    }

    @Override
    public Component getContainer() {
        return container;
    }

    @Override
    public String getScript() {
        return (apv instanceof PageVo) ? ((PageVo) apv).getScript() : null;
    }

    private class RedoTaskListener implements EventListener<Event> {

        RedoTaskListener(IResponseMessage<?> resp) {
            MessageBox.createDialog(resp, this).show();
            if (log.isDebugEnabled())
                log.debug("redo task: {}", taskId);
        }

        @Override
        public void onEvent(Event event) throws Exception {
            TaskService mtc = ServiceLocator
                    .lookup(TaskService.class);
            BeginRequestMessage req = new BeginRequestMessage(
                    new TaskVo(taskId));
            if (event instanceof ActionEvent) {
                ActionEvent evt = (ActionEvent) event;
                req.setId(evt.getId());
                req.setActionFlag(evt.getActionFlag());
            }
            IResponseMessage<?> resp = mtc.begin(req);
            if (!resp.isSuccess()) {
                MessageBox.createDialog(resp, this).show();
            } else if (resp instanceof PageResponseMessage) {
                try {
                    clear();
                    apv = ((PageResponseMessage) resp).getBody();
                    if (apv instanceof PageVo)
                        doPage();
                    else if (apv instanceof BatchVo)
                        doBatch();
                } catch (EasyPlatformWithLabelKeyException ex) {
                    MessageBox.showMessage(ex);
                } catch (BackendException ex) {
                    MessageBox.showMessage(ex.getMsg().getCode(), (String) ex
                            .getMsg().getBody());
                }
            }
        }

    }

    @Override
    public String[] getAccess() {
        return access;
    }
}
